﻿using HDS.Common.Helper;
using HDS.IService.HotelApi;
using HDS.IService.Log;
using HDS.IService.Master;
using HDS.Model;
using HDS.Model.Entity.Log;
using HDS.Model.Entity.Master;
using HDS.Model.Entity.View;
using HDS.SupplierApi.Core;
using HDS.SupplierApi.Core.Entity;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace HDS.Service.HotelApi
{
    public class HotelApiService : IHotelApiService
    {
        private readonly IHttpClientFactory _IHttpClientFactory;
        private readonly ILogger<HotelApiService> _ILogger;
        private readonly IApiLogService _IApiLogService;
        private readonly IDataCacheService _IDataCacheService;
        private readonly IOrderHotelService _IOrderHotelService;
        private readonly IUpdateLogService _IUpdateLogService; 
        private static ConcurrentDictionary<string, ISupplierHotelApi> _ISupplierHotelApiDic = new ConcurrentDictionary<string, ISupplierHotelApi>();

        public HotelApiService(IHttpClientFactory clientFactory, ILogger<HotelApiService> ilogger, IApiLogService iapiLogService, IDataCacheService idataCacheService, IOrderHotelService iorderHotelService, IUpdateLogService iupdateLogService)
        {
            _IHttpClientFactory = clientFactory;
            _ILogger = ilogger;
            _IApiLogService = iapiLogService;
            _IDataCacheService = idataCacheService;
            _IOrderHotelService = iorderHotelService;
            _IUpdateLogService = iupdateLogService;
        }

        public async Task<ResultData<List<DistributorRate>>> SearchRate(string distributor, int hotelId, DateTime checkinDate, DateTime checkoutDate, RoomGuest roomGuest, string currencyCode, string trackId, int millisecondTimeOut)
        {
            var distributorConfig = await _IDataCacheService.GetDistributorConfig(distributor);
            if (!distributorConfig.Isonline)
                return new ResultData<List<DistributorRate>> { Success = false, Code = ErrorCode.Offline, Message = $"{distributor} is offline", Data = null };

            var hotelInfo = await _IDataCacheService.GetHotel(distributor, hotelId);
            if(hotelInfo == null || !hotelInfo.HotelStatus.HasValue || !hotelInfo.HotelStatus.Value || !hotelInfo.Isonline)
                return new ResultData<List<DistributorRate>> { Success = false, Code = ErrorCode.Offline, Message = $"{distributor}-{hotelId} is offline", Data = null };

            var settingList = await _IDataCacheService.GetSupplierSettings(distributor, hotelId, checkinDate, checkoutDate, hotelInfo.CountryCode, hotelInfo.CityCode);
            if(settingList.IsEmpty())
                return new ResultData<List<DistributorRate>> { Success = false, Code = ErrorCode.Offline, Message = $"{distributor}-{hotelId}-supplier is all offline", Data = null };

            var supplierConfigList = await _IDataCacheService.GetSupplierApiConfigs(settingList.Select(e => e.Supplier).ToArray());
            if (supplierConfigList.IsEmpty())
                return new ResultData<List<DistributorRate>> { Success = false, Code = ErrorCode.Offline, Message = $"supplier config is null", Data = null };

            var supRateList = new List<SupplierRate>();
            var cts = new CancellationTokenSource();
            try
            {
                cts.CancelAfter(millisecondTimeOut);

                IEnumerable<Task<List<SupplierRate>>> searchTasks = from supplierConfig in supplierConfigList select SearchSupplierRates(supplierConfig, settingList, checkinDate, checkoutDate, roomGuest, trackId, cts.Token);

                var taskResults = await Task.WhenAll(searchTasks);
                foreach (var result in taskResults)
                {
                    if (!result.IsEmpty())
                        supRateList.AddRange(result);
                }
            }
            catch (TaskCanceledException e)
            {
                _ILogger.LogError(e.Message);
            }
            catch (Exception e)
            {
                _ILogger.LogError(e.Message);
            }
            finally
            {
                cts.Dispose();
            }

            supRateList = SupplierRate.RemoveDuplicate(supRateList);
            
            if(supRateList.IsEmpty())
                return new ResultData<List<DistributorRate>> { Success = false, Code = ErrorCode.NotAvail, Message = "Not available", Data = null };

            var disRateList = await GetDistributorRates(distributorConfig, hotelInfo, supRateList, checkinDate, checkoutDate, roomGuest, currencyCode);
            if (disRateList.IsEmpty())
                return new ResultData<List<DistributorRate>> { Success = false, Code = ErrorCode.NotAvail, Message = "Not available", Data = null };

            return new ResultData<List<DistributorRate>> { Success = true, Data = disRateList };
        }

        public async Task<ResultData<List<SupplierRate>>> SearchRate(string supplier, string[] supplierHotelIds, DateTime checkinDate, DateTime checkoutDate, RoomGuest roomGuest, string trackId, int millisecondTimeOut)
        {
            var supplierConfigList = await _IDataCacheService.GetSupplierApiConfigs(new string[] { supplier });
            if (supplierConfigList.IsEmpty())
                return new ResultData<List<SupplierRate>> { Success = false, Code = ErrorCode.Offline, Message = $"supplier config is null", Data = null };

            var supplierConfig = supplierConfigList[0];
            var supHotelApi = GetSupplierHotelApi(supplierConfig);

            var cts = new CancellationTokenSource();
            try
            {
                cts.CancelAfter(millisecondTimeOut);

                return await supHotelApi.SearchRate(supplierHotelIds, checkinDate, checkoutDate, roomGuest, trackId, cts.Token);
            }
            catch (TaskCanceledException e)
            {
                _ILogger.LogError(e.Message);

                return new ResultData<List<SupplierRate>> { Success = false, Code = ErrorCode.Timeout, Message = e.Message, Data = null };
            }
            catch (Exception e)
            {
                _ILogger.LogError(e.Message);

                return new ResultData<List<SupplierRate>> { Success = false, Code = ErrorCode.SystemError, Message = e.Message, Data = null };
            }
            finally
            {
                cts.Dispose();
            }
        }

        public async Task<ResultData<DistributorRate>> ConfirmRate(string distributor, int hotelId, DateTime checkinDate, DateTime checkoutDate, RoomGuest roomGuest, int roomId, string ratePlanId, string currencyCode, string trackId, int millisecondTimeOut)
        {
            return await ConfirmRate(distributor, hotelId, checkinDate, checkoutDate, roomGuest, roomId, ratePlanId, currencyCode, trackId, null, millisecondTimeOut);
        }

        public async Task<ResultData<SupplierOrderInfo>> Reservation(string distributor, int hotelId, DateTime checkinDate, DateTime checkoutDate, RoomGuest roomGuest, int roomId, string ratePlanId, string distributorOrderId, string orderId, Money price, string guestRequest, int millisecondTimeOut, int updatedBy = 0, string updatedByName = "system")
        {
            ResultData<SupplierOrderInfo> resultData;
               var confirmResultData = await ConfirmRate(distributor, hotelId, checkinDate, checkoutDate, roomGuest, roomId, ratePlanId, price.Currency, orderId, orderId, millisecondTimeOut);
            if (!confirmResultData.Success)
            {
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = confirmResultData.Code, Message = confirmResultData.Message, Data = null };

                await _IUpdateLogService.Add(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "Reservation",
                    LogText = $"预订失败 {ratePlanId}: " + confirmResultData.Message,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now
                });

                return resultData;
            }

            var disRate = confirmResultData.Data;
            if (price.Amount < disRate.CostPrice.Amount)
            {
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.PriceLoss, Message = $"Price change {disRate.Price.Amount}" };

                await _IUpdateLogService.Add(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "Reservation",
                    LogText = $"预订失败 {disRate.SupplierRate.Supplier}: " + confirmResultData.Message,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now
                });

                return resultData;
            }

            var supplierConfigList = await _IDataCacheService.GetSupplierApiConfigs(new string[] { disRate.SupplierRate.Supplier });
            if (supplierConfigList.IsEmpty())
            { 
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.Offline, Message = $"{disRate.SupplierRate.Supplier} config is null", Data = null };

                await _IUpdateLogService.Add(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "Reservation",
                    LogText = $"预订失败 {disRate.SupplierRate.Supplier}: " + confirmResultData.Message,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now
                });

                return resultData;
            }

            var supplierConfig = supplierConfigList[0];
            var supHotelApi = GetSupplierHotelApi(supplierConfig);
            if (supHotelApi == null)
            { 
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.Offline, Message = $"{disRate.SupplierRate.Supplier} api is null", Data = null };

                await _IUpdateLogService.Add(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "Reservation",
                    LogText = $"预订失败 {disRate.SupplierRate.Supplier}: " + confirmResultData.Message,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now
                });

                return resultData;
            }

            var cts = new CancellationTokenSource();
            try
            {
                cts.CancelAfter(millisecondTimeOut);

                resultData = await supHotelApi.Reservation(disRate.SupplierRate, checkinDate, checkoutDate, roomGuest, orderId, guestRequest, cts.Token);
                if (resultData.Success)
                {
                    var supOrder = resultData.Data;

                    await _IUpdateLogService.Add(new UpdateLog
                    {
                        LogType = UpdateLogType.Order,
                        LogSubType = "Reservation",
                        LogText = $"预订成功 {supOrder.Supplier}-{supOrder.SupplierOrderId}",
                        BusinessId = orderId,
                        UpdatedBy = updatedBy,
                        UpdatedByName = updatedByName,
                        UpdatedTime = DateTime.Now
                    });

                    await _IOrderHotelService.InsertSupplierOrder(new OrderHotelSupplier
                    {
                        Id = 0,
                        OrderId = orderId,
                        Supplier = supOrder.Supplier,
                        SupplierOrderId = supOrder.SupplierOrderId,
                        SupplierOrderStatus = supOrder.SupplierOrderStatus,
                        SupplierErrorCode = (int)resultData.Code,
                        SupplierHotelId = supOrder.SupplierRate.HotelId,
                        SupplierRoomId = supOrder.SupplierRate.RoomId,
                        SupplierRoomName = supOrder.SupplierRate.RoomName,
                        SupplierMealType = supOrder.SupplierRate.MealCode,
                        SupplierMealDesc = supOrder.SupplierRate.MealDesc,
                        SupplierMealCount = supOrder.SupplierRate.MealCount,
                        SupplierRateplanId = supOrder.SupplierRate.RatePlanId,
                        SupplierCurrency = supOrder.SettlementPrice.Currency,
                        SupplierPrice = supOrder.SettlementPrice.Amount,
                        SupplierFreeCancelTime = DateTimeHelper.GetFreeCancellationTime(checkinDate, supOrder.SupplierRate.FreeCancellationTimeLimit),
                        SupplierExtrasJson = supOrder.SupplierRate.Extras,
                        SupplierRemark = supOrder.SupplierRate.Remarks,
                        UpdatedBy = updatedBy,
                        UpdatedByName = updatedByName,
                        UpdatedTime = DateTime.Now,
                    });

                    await _IOrderHotelService.UpdateOrderStatus(orderId, supOrder.SupplierOrderStatus);

                    if (!string.IsNullOrEmpty(supOrder.HotelConfirmNo))
                    {
                        await _IUpdateLogService.Add(new UpdateLog
                        {
                            LogType = UpdateLogType.Order,
                            LogSubType = "UpdateHotelConfirmNo",
                            LogText = $"更新酒店确认号: [{supOrder.HotelConfirmNo}]",
                            BusinessId = orderId,
                            UpdatedBy = updatedBy,
                            UpdatedByName = updatedByName,
                            UpdatedTime = DateTime.Now,
                        });

                        await _IOrderHotelService.UpdateHotelConfirmNo(orderId, supOrder.HotelConfirmNo);
                    }
                }
                else
                {
                    await _IUpdateLogService.Add(new UpdateLog
                    {
                        LogType = UpdateLogType.Order,
                        LogSubType = "Reservation",
                        LogText = $"预订失败 {disRate.SupplierRate.Supplier}: " + resultData.Message,
                        BusinessId = orderId,
                        UpdatedBy = updatedBy,
                        UpdatedByName = updatedByName,
                        UpdatedTime = DateTime.Now
                    });

                    await _IOrderHotelService.InsertSupplierOrder(new OrderHotelSupplier
                    {
                        Id = 0,
                        OrderId = orderId,
                        Supplier = disRate.SupplierRate.Supplier,
                        SupplierOrderId = "",
                        SupplierOrderStatus = OrderHotelStatus.Fail,
                        SupplierErrorCode = (int)resultData.Code,
                        SupplierHotelId = disRate.SupplierRate.HotelId,
                        SupplierRoomId = disRate.SupplierRate.RoomId,
                        SupplierRoomName = disRate.SupplierRate.RoomName,
                        SupplierMealType = disRate.SupplierRate.MealCode,
                        SupplierMealDesc = disRate.SupplierRate.MealDesc,
                        SupplierMealCount = disRate.SupplierRate.MealCount,
                        SupplierRateplanId = disRate.SupplierRate.RatePlanId,
                        SupplierCurrency = disRate.SupplierRate.NetPrice.Currency,
                        SupplierPrice = disRate.SupplierRate.NetPrice.Amount,
                        SupplierFreeCancelTime = DateTimeHelper.GetFreeCancellationTime(checkinDate, disRate.SupplierRate.FreeCancellationTimeLimit),
                        SupplierExtrasJson = disRate.SupplierRate.Extras,
                        SupplierRemark = disRate.SupplierRate.Remarks,
                        UpdatedBy = updatedBy,
                        UpdatedByName = updatedByName,
                        UpdatedTime = DateTime.Now,
                    });

                    await _IOrderHotelService.UpdateOrderStatus(orderId, OrderHotelStatus.Fail);
                }
                return resultData;
            }
            catch (TaskCanceledException e)
            {
                _ILogger.LogError(e.Message);
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.Timeout, Message = e.Message, Data = null };
            }
            catch (Exception e)
            {
                _ILogger.LogError(e.Message);
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.SystemError, Message = e.Message, Data = null };
            }
            finally
            {
                cts.Dispose();
            }

            await _IUpdateLogService.Add(new UpdateLog
            {
                LogType = UpdateLogType.Order,
                LogSubType = "Reservation",
                LogText = $"预订失败 {disRate.SupplierRate.Supplier}: " + resultData.Message,
                BusinessId = orderId,
                UpdatedBy = updatedBy,
                UpdatedByName = updatedByName,
                UpdatedTime = DateTime.Now
            });

            return resultData;
        }

        public async Task<ResultData<int>> CancelDistributorOrder(string distributor, string distributorOrderId, int millisecondTimeOut, int updatedBy = 0, string updatedByName = "system")
        {
            var disOrder = await _IOrderHotelService.GetDistributorOrder(distributor, distributorOrderId);
            if (disOrder == null)
                return new ResultData<int> { Success = false, Code = ErrorCode.NotFind, Message = "Order not found" };

            await _IOrderHotelService.UpdateDistributorOrderStatus(disOrder.OrderId, DistributorOrderStatus.Canceled, updatedBy, updatedByName, $"取消订单 {distributor}-{distributorOrderId}");

            if (disOrder.OrderStatus == OrderHotelStatus.Canceled)
            {
                return new ResultData<int> { Success = true };
            }

            var supOrderList = await _IOrderHotelService.GetSupplierOrderList(disOrder.OrderId);
            foreach (var supOrder in supOrderList)
            {
                if (supOrder.SupplierOrderStatus == OrderHotelStatus.Canceled || string.IsNullOrEmpty(supOrder.SupplierOrderId))
                    continue;

                var resultData = await CancelSupplierOrder(supOrder.Supplier, supOrder.SupplierOrderId, disOrder.OrderId, supOrder.SupplierExtrasJson, millisecondTimeOut, updatedBy, updatedByName);
                if (!resultData.Success)
                    return new ResultData<int> { Success = false, Code = resultData.Code, Message = resultData.Message };
            }

            if(await _IOrderHotelService.UpdateOrderStatus(disOrder.OrderId, OrderHotelStatus.Canceled))
                return new ResultData<int> { Success = true };
            else
                return new ResultData<int> { Success = false, Code = ErrorCode.DataValidationFailed, Message = "Unknown error" };
        }

        public async Task<ResultData<int>> CancelSupplierOrder(string supplier, string supplierOrderId, string orderId, Dictionary<string, string> supplierExtras, int millisecondTimeOut, int updatedBy = 0, string updatedByName = "system")
        {
            var supplierConfigList = await _IDataCacheService.GetSupplierApiConfigs(new string[] { supplier });
            if (supplierConfigList.IsEmpty())
                return new ResultData<int> { Success = false, Code = ErrorCode.Offline, Message = $"{supplier} config is null" };

            var supplierConfig = supplierConfigList[0];
            var supHotelApi = GetSupplierHotelApi(supplierConfig);
            if (supHotelApi == null)
                return new ResultData<int> { Success = false, Code = ErrorCode.Offline, Message = $"{supplier} api is null" };

            ResultData<SupplierOrderInfo> resultData;
            var cts = new CancellationTokenSource();
            try
            {
                cts.CancelAfter(millisecondTimeOut);

                resultData = await supHotelApi.Cancel(supplierOrderId, orderId, supplierExtras, cts.Token);
            }
            catch (TaskCanceledException e)
            {
                _ILogger.LogError(e.Message);
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.Timeout, Message = e.Message, Data = null };
            }
            catch (Exception e)
            {
                _ILogger.LogError(e.Message);
                resultData = new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.SystemError, Message = e.Message, Data = null };
            }
            finally
            {
                cts.Dispose();
            }

            if (!resultData.Success)
            {
                await _IUpdateLogService.Add(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "Cancel",
                    LogText = $"订单取消失败 {supplier}-{supplierOrderId}: {resultData.Message}",
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now
                });
            }

            if (resultData.Data == null)
            {
                await _IUpdateLogService.Add(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "Cancel",
                    LogText = $"订单取消失败 {supplier}-{supplierOrderId}: SupplierOrderInfo is null",
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now
                });
            }

            if (resultData.Data.SupplierOrderStatus == OrderHotelStatus.Canceled)
            {
                var logText = $"订单取消成功 {supplier}-{supplierOrderId}";

                await _IOrderHotelService.UpdateSupplierOrder(orderId, supplier, supplierOrderId, resultData.Data.SupplierOrderStatus, resultData.Data.SettlementPrice, updatedBy, updatedByName, logText);
            }
            else if (resultData.Data.SupplierOrderStatus == OrderHotelStatus.Canceling)
            {
                var logText = $"订单取消中 {supplier}-{supplierOrderId}";

                await _IOrderHotelService.UpdateSupplierOrderStatus(orderId, supplier, supplierOrderId, resultData.Data.SupplierOrderStatus, updatedBy, updatedByName, logText);
            }
            else
            {
                await _IUpdateLogService.Add(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "Cancel",
                    LogText = $"订单取消失败 {supplier}-{supplierOrderId}: 状态异常",
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now
                });
            }

            return new ResultData<int> { Success = resultData.Success, Code = resultData.Code, Message = resultData.Message };
        }

        public async Task<ResultData<SupplierOrderInfo>> GetOrder(string orderId, string supplier, string supplierOrderId, Dictionary<string, string> extras, int millisecondTimeOut)
        {
            var supplierConfigList = await _IDataCacheService.GetSupplierApiConfigs(new string[] { supplier });
            if (supplierConfigList.IsEmpty())
                return new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.Offline, Message = $"{supplier} config is null", Data = null };

            var supplierConfig = supplierConfigList[0];
            var supHotelApi = GetSupplierHotelApi(supplierConfig);
            if (supHotelApi == null)
                return new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.Offline, Message = $"{supplier} api is null", Data = null };

            var cts = new CancellationTokenSource();
            try
            {
                cts.CancelAfter(millisecondTimeOut);

                return await supHotelApi.GetOrder(supplierOrderId, orderId, extras, cts.Token);
            }
            catch (TaskCanceledException e)
            {
                _ILogger.LogError(e.Message);
                return new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.Timeout, Message = e.Message, Data = null };
            }
            catch (Exception e)
            {
                _ILogger.LogError(e.Message);
                return new ResultData<SupplierOrderInfo> { Success = false, Code = ErrorCode.SystemError, Message = e.Message, Data = null };
            }
            finally
            {
                cts.Dispose();

            }
        }




        private async Task<ResultData<DistributorRate>> ConfirmRate(string distributor, int hotelId, DateTime checkinDate, DateTime checkoutDate, RoomGuest roomGuest, int roomId, string ratePlanId, string currencyCode, string trackId, string orderId, int millisecondTimeOut)
        {
            var distributorConfig = await _IDataCacheService.GetDistributorConfig(distributor);
            if (!distributorConfig.Isonline)
                return new ResultData<DistributorRate> { Success = false, Code = ErrorCode.Offline, Message = $"{distributor} is offline", Data = null };

            var hotelInfo = await _IDataCacheService.GetHotel(distributor, hotelId);
            if (hotelInfo == null || !hotelInfo.HotelStatus.HasValue || !hotelInfo.HotelStatus.Value || !hotelInfo.Isonline)
                return new ResultData<DistributorRate> { Success = false, Code = ErrorCode.Offline, Message = $"{distributor}-{hotelId} is offline", Data = null };

            var settingList = await _IDataCacheService.GetSupplierSettings(distributor, hotelId, checkinDate, checkoutDate, hotelInfo.CountryCode, hotelInfo.CityCode);
            if (settingList.IsEmpty())
                return new ResultData<DistributorRate> { Success = false, Code = ErrorCode.Offline, Message = $"{distributor}-{hotelId}-supplier is all offline", Data = null };

            var supplierConfigList = await _IDataCacheService.GetSupplierApiConfigs(settingList.Select(e => e.Supplier).ToArray());
            if (supplierConfigList.IsEmpty())
                return new ResultData<DistributorRate> { Success = false, Code = ErrorCode.Offline, Message = "supplier config is null", Data = null };

            var supplierRate = SupplierRate.BreakupRatePlanId(ratePlanId, supplierConfigList.ToDictionary(e => e.SupplierAbbr, e => e.Supplier));
            if (supplierRate == null)
                return new ResultData<DistributorRate> { Success = false, Code = ErrorCode.Offline, Message = $"{ratePlanId} is null", Data = null };

            var supplierConfig = supplierConfigList.Where(e => e.Supplier.Equals(supplierRate.Supplier)).FirstOrDefault();

            var supHotelApi = GetSupplierHotelApi(supplierConfig);
            if (supHotelApi == null)
                return null;

            var setting = settingList.Where(e => e.Supplier.Equals(supplierConfig.Supplier) && !string.IsNullOrEmpty(e.SupplierHotelId)).FirstOrDefault();
            if (setting == null)
                return null;

            var cts = new CancellationTokenSource();
            try
            {
                cts.CancelAfter(millisecondTimeOut);

                var resultData = await supHotelApi.ConfirmRate(supplierRate.HotelId, checkinDate, checkoutDate, roomGuest, ratePlanId, trackId, orderId, cts.Token);
                if (!resultData.Success)
                    return new ResultData<DistributorRate> { Success = false, Code = resultData.Code, Message = resultData.Message, Data = null };

                if (resultData.Data == null)
                    return new ResultData<DistributorRate> { Success = false, Code = ErrorCode.ConfirmFailure, Message = "Confirm failure", Data = null };

                supplierRate = resultData.Data;
            }
            catch (TaskCanceledException e)
            {
                _ILogger.LogError(e.Message);
            }
            catch (Exception e)
            {
                _ILogger.LogError(e.Message);
            }
            finally
            {
                cts.Dispose();
            }

            var disRateList = await GetDistributorRates(distributorConfig, hotelInfo, new List<SupplierRate> { supplierRate }, checkinDate, checkoutDate, roomGuest, currencyCode);
            if (disRateList.IsEmpty())
                return new ResultData<DistributorRate> { Success = false, Code = ErrorCode.ConfirmFailure, Message = "Confirm failure", Data = null };

            return new ResultData<DistributorRate> { Success = true, Data = disRateList[0] };
        }

        private async Task<List<DistributorRate>> GetDistributorRates(DistributorConfig distributorConfig, VDistributorHotel hotelInfo, List<SupplierRate> supRateList, DateTime checkinDate, DateTime checkoutDate, RoomGuest roomGuest, string currencyCode)
        {
            var exrates = await _IDataCacheService.GetExchangeRates();
            var commissions = await _IDataCacheService.GetHotelCommissions(hotelInfo.Distributor, hotelInfo.HotelId, checkinDate, checkoutDate, hotelInfo.CountryCode, hotelInfo.CityCode);
            var nightNum = DateTimeHelper.GetNights(checkinDate, checkoutDate) * roomGuest.Count;

            var roomMaps = await _IDataCacheService.GetRoomMappings(hotelInfo.HotelId);
            var disRates = new List<DistributorRate>();
            foreach (var supRate in supRateList)
            {
                var disRate = new DistributorRate();
                disRate.SupplierRate = supRate;
                disRate.HotelId = hotelInfo.HotelId;

                var roomMap = roomMaps.Where(e => e.Supplier.Equals(supRate.Supplier) && e.SupplierHotelId.Equals(supRate.HotelId) && e.SupplierRoomId.Equals(supRate.RoomId)).FirstOrDefault();
                if (roomMap != null)
                {
                    disRate.RoomId = roomMap.RoomId;
                    disRate.RoomNameCn = roomMap.RoomNameCn;
                    disRate.RoomNameEn = roomMap.RoomNameEn;
                }

                #region 转换汇率

                disRate.DailyPrices = new List<DailyPrice>();
                var exrate = exrates.Where(e => e.CodeFrom.Equals(supRate.NetPrice.Currency) && e.CodeTo.Equals(currencyCode)).FirstOrDefault();
                if (exrate != null)
                {
                    foreach (var dailyPrice in supRate.DailyPrices)
                    {
                        disRate.DailyPrices.Add(new DailyPrice
                        {
                            Date = dailyPrice.Date,
                            Price = dailyPrice.Price * exrate.Rate,
                            SellingPrice = dailyPrice.SellingPrice * exrate.Rate
                        });
                    }

                    disRate.Price = new Money
                    {
                        Currency = currencyCode,
                        Amount = disRate.DailyPrices.Sum(e => e.Price)
                    };

                    if (supRate.SellingPrice != null)
                        disRate.SellingPrice = new Money
                        {
                            Currency = currencyCode,
                            Amount = disRate.DailyPrices.Sum(e => e.SellingPrice)
                        };
                }
                else
                {
                    foreach (var dailyPrice in supRate.DailyPrices)
                    {
                        disRate.DailyPrices.Add(new DailyPrice
                        {
                            Date = dailyPrice.Date,
                            Price = dailyPrice.Price,
                            SellingPrice = dailyPrice.SellingPrice
                        });
                    }

                    disRate.Price = new Money
                    {
                        Currency = supRate.NetPrice.Currency,
                        Amount = supRate.NetPrice.Amount
                    };

                    if (supRate.SellingPrice != null)
                        disRate.SellingPrice = new Money
                        {
                            Currency = supRate.SellingPrice.Currency,
                            Amount = supRate.SellingPrice.Amount
                        };
                }

                #endregion

                #region 计算佣金

                var _commission = GetCommission(supRate.Supplier, supRate.NetPrice.Amount, commissions);
                if (distributorConfig.IsSellingPrice == 1) //卖价抽佣模式
                {
                    if (_commission.PlusRateType == 0)
                    {
                        disRate.CostPrice = new Money
                        {
                            Currency = disRate.Price.Currency,
                            Amount = (disRate.Price.Amount / (decimal)(1 - distributorConfig.CommissionRate)) * (1 - _commission.LossRate)
                        };

                        foreach (var dailyPrice in disRate.DailyPrices)
                        {
                            dailyPrice.Price = dailyPrice.Price * (1 + _commission.PlusRate) / (decimal)(1 - distributorConfig.CommissionRate);
                        }
                    }
                    else
                    {
                        disRate.CostPrice = new Money
                        {
                            Currency = disRate.Price.Currency,
                            Amount = disRate.Price.Amount / (decimal)(1 - distributorConfig.CommissionRate) - _commission.LossRate * nightNum
                        };

                        foreach (var dailyPrice in disRate.DailyPrices)
                        {
                            dailyPrice.Price = (dailyPrice.Price + _commission.PlusRate * nightNum) / (decimal)(1 - distributorConfig.CommissionRate);
                        }
                    }

                    disRate.Price.Amount = disRate.DailyPrices.Sum(e => e.Price);
                }
                else
                {
                    if (_commission.PlusRateType == 0)
                    {
                        disRate.CostPrice = new Money
                        {
                            Currency = disRate.Price.Currency,
                            Amount = disRate.Price.Amount * (1 - _commission.LossRate)
                        };

                        foreach (var dailyPrice in disRate.DailyPrices)
                        {
                            dailyPrice.Price = dailyPrice.Price * (1 + _commission.PlusRate);
                        }
                    }
                    else
                    {
                        disRate.CostPrice = new Money
                        {
                            Currency = disRate.Price.Currency,
                            Amount = disRate.Price.Amount - _commission.LossRate * nightNum
                        };

                        foreach (var dailyPrice in disRate.DailyPrices)
                        {
                            dailyPrice.Price = dailyPrice.Price + _commission.PlusRate * nightNum;
                        }
                    }

                    disRate.Price.Amount = disRate.DailyPrices.Sum(e => e.Price);
                }

                #endregion

                if (supRate.FreeCancellationTimeLimit < GlobalVariable.NON_REFUNDABLE_HOURS)
                    disRate.FreeCancellationTimeLimit = supRate.FreeCancellationTimeLimit + distributorConfig.PlusCancelHour;
                else
                    disRate.FreeCancellationTimeLimit = GlobalVariable.NON_REFUNDABLE_HOURS;

                disRates.Add(disRate);
            }

            return disRates;
        }

        public HotelCommission GetCommission(string supplier, decimal price, List<HotelCommission> commissions)
        {
            var _commissions = commissions.Where(e => e.Supplier.Equals(supplier));
            if (_commissions.IsEmpty())
                return new HotelCommission
                {
                    PlusRateType = 0,
                    PlusRate = 0.01M,
                    LossRate = 0
                };

            var _comm = _commissions.Where(e => (e.MinPrice.HasValue && price >= e.MinPrice.Value) && (e.MaxPrice.HasValue && price < e.MaxPrice.Value)).FirstOrDefault();
            if (_comm != null)
                return _comm;
            else
                return _commissions.FirstOrDefault();
        }

        private async Task<List<SupplierRate>> SearchSupplierRates(SupplierApiConfig supplierConfig, List<DistributorSupplierSetting> settingList, DateTime checkinDate, DateTime checkoutDate, RoomGuest roomGuest, string trackId, CancellationToken cancelToken)
        {
            var supHotelApi = GetSupplierHotelApi(supplierConfig);
            if (supHotelApi == null)
                return null;

            var setting = settingList.Where(e => e.Supplier.Equals(supplierConfig.Supplier) && !string.IsNullOrEmpty(e.SupplierHotelId)).FirstOrDefault();
            if (setting == null)
                return null;

            if (setting.UseCache)//使用自定义缓存
            {
                return null;//未完成
            }
            else
            {
                var resultData = await supHotelApi.SearchRate(setting.SupplierHotelId, checkinDate, checkoutDate, roomGuest, trackId, cancelToken);
                if (resultData.Success)
                    return resultData.Data;
                else
                    return null;
            }
        }

        private ISupplierHotelApi GetSupplierHotelApi(SupplierApiConfig supplierConfig)
        {
            if (supplierConfig == null)
                return null;

            if (!_ISupplierHotelApiDic.TryGetValue(supplierConfig.Supplier, out ISupplierHotelApi _isupplierHotelApi))
            {
                var httpClient = _IHttpClientFactory.CreateClient("suppliers");
                switch (supplierConfig.SupplierGroup)
                {
                    case "hotelbeds":
                        _isupplierHotelApi = new HDS.SupplierApi.Hotelbeds.SupplierHotelApi(httpClient, _ILogger, _IApiLogService);
                        break;
                    case "miki":
                        _isupplierHotelApi = new HDS.SupplierApi.Miki.SupplierHotelApi(httpClient, _ILogger, _IApiLogService);
                        break;
                }

                if (_isupplierHotelApi != null)
                {
                    _isupplierHotelApi.LoadApiConfig(supplierConfig);
                    _ISupplierHotelApiDic.TryAdd(supplierConfig.Supplier, _isupplierHotelApi);
                }
            }

            return _isupplierHotelApi;
        }
    }
}
